<?php

/*
 * This file is part of SwiftMailer.
 * (c) 2004-2009 Chris Corbyn
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Redundantly and rotationally uses several Transports when sending.
 *
 * @package    Swift
 * @subpackage Transport
 * @author     Chris Corbyn
 */
class Swift_Transport_LoadBalancedTransport implements Swift_Transport
{
	/**
	 * Transports which are deemed useless.
	 *
	 * @var Swift_Transport[]
	 */
	private $_deadTransports = array();

	/**
	 * The Transports which are used in rotation.
	 *
	 * @var Swift_Transport[]
	 */
	protected $_transports = array();

	/**
	 * Creates a new LoadBalancedTransport.
	 */
	public function __construct()
	{
	}

	/**
	 * Set $transports to delegate to.
	 *
	 * @param Swift_Transport[] $transports
	 */
	public function setTransports(array $transports)
	{
		$this->_transports = $transports;
		$this->_deadTransports = array();
	}

	/**
	 * Get $transports to delegate to.
	 *
	 * @return Swift_Transport[]
	 */
	public function getTransports()
	{
		return array_merge($this->_transports, $this->_deadTransports);
	}

	/**
	 * Test if this Transport mechanism has started.
	 *
	 * @return boolean
	 */
	public function isStarted()
	{
		return count($this->_transports) > 0;
	}

	/**
	 * Start this Transport mechanism.
	 */
	public function start()
	{
		$this->_transports = array_merge($this->_transports, $this->_deadTransports);
	}

	/**
	 * Stop this Transport mechanism.
	 */
	public function stop()
	{
		foreach ($this->_transports as $transport) {
			$transport->stop();
		}
	}

	/**
	 * Send the given Message.
	 *
	 * Recipient/sender data will be retrieved from the Message API.
	 * The return value is the number of recipients who were accepted for delivery.
	 *
	 * @param Swift_Mime_Message $message
	 * @param string[] $failedRecipients An array of failures by-reference
	 *
	 * @return int
	 */
	public function send(Swift_Mime_Message $message, &$failedRecipients = null)
	{
		$maxTransports = count($this->_transports);
		$sent = 0;

		for ($i = 0; $i < $maxTransports
		&& $transport = $this->_getNextTransport(); ++$i) {
			try {
				if (!$transport->isStarted()) {
					$transport->start();
				}
				if ($sent = $transport->send($message, $failedRecipients)) {
					break;
				}
			} catch (Swift_TransportException $e) {
				$this->_killCurrentTransport();
			}
		}

		if (count($this->_transports) == 0) {
			throw new Swift_TransportException(
				'All Transports in LoadBalancedTransport failed, or no Transports available'
			);
		}

		return $sent;
	}

	/**
	 * Register a plugin.
	 *
	 * @param Swift_Events_EventListener $plugin
	 */
	public function registerPlugin(Swift_Events_EventListener $plugin)
	{
		foreach ($this->_transports as $transport) {
			$transport->registerPlugin($plugin);
		}
	}

	// -- Protected methods

	/**
	 * Rotates the transport list around and returns the first instance.
	 *
	 * @return Swift_Transport
	 */
	protected function _getNextTransport()
	{
		if ($next = array_shift($this->_transports)) {
			$this->_transports[] = $next;
		}

		return $next;
	}

	/**
	 * Tag the currently used (top of stack) transport as dead/useless.
	 */
	protected function _killCurrentTransport()
	{
		if ($transport = array_pop($this->_transports)) {
			try {
				$transport->stop();
			} catch (Exception $e) {
			}
			$this->_deadTransports[] = $transport;
		}
	}
}
